﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ServiceModel.Syndication;
using System.Xml;
using System.IO;
using System.Net.NetworkInformation;

namespace IkeCode.WP7.Toolkit.HttpRequest
{
    /// <summary>
    /// Class that contains async web requests
    /// </summary>
    public class AsyncCall
    {
        static AsyncCall _instance;
        /// <summary>
        /// Return an instance of AsyncCall
        /// </summary>
        public static AsyncCall Get
        {
            get
            {
                _instance = _instance == null ? _instance = new AsyncCall() : _instance;
                return _instance;
            }
        }

        /// <summary>
        /// Get byte[] from Uri and return
        /// </summary>
        /// <param name="url">Uri object</param>
        /// <param name="callback">Callback action with byte[], Content Type string and Exception objects</param>
        /// <exception cref="IkeCode.WP7.Toolkit.InternetConnectionRequiredException">Throw when internet connection fails</exception>
        public void Bytes(Uri url, Action<byte[], string, Exception> callback)
        {
            try
            {
                this.ValidateCall(url);

                HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url.NormalizeUrl());
                request.BeginGetResponse(new AsyncCallback((asyncResult) =>
                {
                    string data = string.Empty;
                    SyndicationFeed feed = new SyndicationFeed();

                    using (WebResponse response = (asyncResult.AsyncState as HttpWebRequest).EndGetResponse(asyncResult) as HttpWebResponse)
                    {
                        var stream = response.GetResponseStream();
                        using (BinaryReader reader = new BinaryReader(stream))
                        {
                            var bytes = reader.ReadBytes(500000);
                            reader.Close();
                            callback(bytes, response.ContentType, null);
                        }

                        stream.Close();
                        response.Close();
                    }
                }), request);
            }
            catch (Exception e)
            {
                callback(null, null, e);
            }
        }

        /// <summary>
        /// Get stream from Uri and return
        /// </summary>
        /// <param name="url">Uri object</param>
        /// <param name="callback">Callback action with StreamReader, Content Type string and Exception objects</param>
        /// <exception cref="IkeCode.WP7.Toolkit.InternetConnectionRequiredException">Throw when internet connection fails</exception>
        public void Stream(Uri url, Action<StreamReader, string, Exception> callback)
        {
            try
            {
                this.ValidateCall(url);

                HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url.NormalizeUrl());
                request.BeginGetResponse(new AsyncCallback((asyncResult) =>
                {
                    string data = string.Empty;
                    SyndicationFeed feed = new SyndicationFeed();

                    using (WebResponse response = (asyncResult.AsyncState as HttpWebRequest).EndGetResponse(asyncResult) as HttpWebResponse)
                    using (StreamReader reader = new StreamReader(response.GetResponseStream()))
                    {
                        callback(reader, response.ContentType, null);
                    }
                }), request);
            }
            catch (Exception e)
            {
                callback(null, null, e);
            }
        }

        /// <summary>
        /// Get string from Uri and return
        /// </summary>
        /// <param name="url">Uri object</param>
        /// <param name="callback">Callback action</param>
        /// <exception cref="IkeCode.WP7.Toolkit.InternetConnectionRequiredException">Throw when internet connection fails</exception>
        public void String(Uri url, Action<string, Exception> callback)
        {
            this.Stream(url, (stream, contentType, error) =>
            {
                if (error != null) { callback(null, error); }
                else
                {
                    var result = stream.ReadToEnd();
                    callback(result, null);
                }
            });
        }

        /// <summary>
        /// Get string from Uri and return a SyndicationFeed object
        /// </summary>
        /// <param name="url">Uri object</param>
        /// <param name="callback">Callback action</param>
        /// <exception cref="IkeCode.WP7.Toolkit.InternetConnectionRequiredException">Throw when internet connection fails</exception>
        public void Syndication(Uri url, Action<SyndicationFeed, Exception> callback)
        {
            this.Stream(url, (stream, contentType, error) =>
            {
                if (error != null) { callback(null, error); }
                else
                {
                    SyndicationFeed feed = new SyndicationFeed();

                    XmlReader xmlReader = XmlReader.Create(stream);
                    feed = SyndicationFeed.Load(xmlReader);

                    callback(feed, null);
                }
            });
        }

        private bool ValidateCall(Uri url)
        {
            var result = true;

            if (url == null)
                throw new ArgumentNullException("Url cannot be null");

            if (!NetworkInterface.GetIsNetworkAvailable())
                throw new InternetConnectionRequiredException();

            return result;
        }
    }
}
